#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ppp_ctrl.h"
#include "atchannel.h"
#include <telephony/dongle.h>
#include "uevent.h"
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include "modem_usb.h"
#include "sunxi_ril.h"
#include "dongle_util.h"
#define LOG_TAG "DONGLE"
#include <utils/Log.h>

static int modem_usb_get_all_devices_by_prefix(char device[][MAX_PATH_LEN],  int* count, const char* prefix);


static int modem_info_same_vid_pid(struct modem_info* cur, struct modem_info* next)
{
	return cur->pid_vid_now == next->pid_vid_now
				&& cur->pid_vid_origin == next->pid_vid_origin;
}

static int modem_info_same_port(struct modem_info* cur, struct modem_info* next)
{
	return cur->at_port == next->at_port && cur->ppp_port == next->ppp_port;
}

static int _modem_info_fix_port_index(int index)
{
	return (index >= SMALLEST_ACM_DEVICE_INDEX) ? 
		index - SMALLEST_ACM_DEVICE_INDEX : index;
}

int modem_info_fix_port_index(struct modem_info* info, int at)
{
	return _modem_info_fix_port_index(at ? info->at_port : info->ppp_port);
}

const char* modem_info_get_device_prefix(struct modem_info* info)
{
	return info->at_port >= SMALLEST_ACM_DEVICE_INDEX ? "ttyACM" : "ttyUSB";
}

void modem_info_get_port_path(struct modem_info* info, int at, char* path)
{
	const char* prefix =  modem_info_get_device_prefix(info);
	int index = modem_info_fix_port_index(info, at);
	//snprintf(path, 32, "/dev/%s%d", prefix, index);
	modem_usb_get_port_path_by_prefix(NULL, prefix, index, path);	
}

void modem_info_dump(struct modem_info* info)
{
#ifdef ENABLE_LOG
	ALOGD("%s: name:%s pid-vid-origin:%x pid-vid-now:%x at-port:%d ppp-port:%d\n",
			__func__, info->dongle_name, info->pid_vid_origin, info->pid_vid_now,
			info->at_port, info->ppp_port);
#endif		
}

void initial_sleep_by_pid_vid(unsigned int pid_vid)
{
	//FIXME check how long need to sleep
	//nomally 5 seconds
	sleep(5);
}

int modem_usb_get_all_devices(char device[][MAX_PATH_LEN], int* count)
{
	int ret = 0;
	/* NOTE:
	 * 		/dev/ttyACM has high priority
	 * 	for somme dongle, both exist ACM and USB, 
	 * 	but only ACM devices are useful 
	 */
	if((ret = modem_usb_get_all_devices_by_prefix(device, count, "ttyACM")) > 0)
	{
		return ret;
	}
	
	return modem_usb_get_all_devices_by_prefix(device, count, "ttyUSB");
}

static int modem_usb_get_all_devices_by_prefix(char device[][MAX_PATH_LEN], int* count, const char* prefix)
{
	int ret = -1;
	FILE* fp = NULL;
	char device_uevent_file[64];
	int i = 0;
	do 
	{
		snprintf(device_uevent_file, 64, MODEM_USBSERIAL_UEVENT_FILE, prefix, i);
		if((fp = fopen(device_uevent_file, "r")) != NULL)
		{
			char* line = NULL;
			int len = 0;
			int read = 0;
#ifdef ENABLE_LOG
			ALOGD("%s: Try open %s", __func__, device_uevent_file);
#endif		
			while((read = modem_getline(&line, &len, fp)) > 0) 
			{
				if(strStartsWith(line, "DEVNAME="))
				{
					memset(device[*count], 0, MAX_PATH_LEN);
					strncpy(device[*count], strstr(line, "=") + strlen("="), strlen("ttyUSB0"));
#ifdef ENABLE_LOG
			ALOGD("%s: line:%s Get device %s, count:%d", __func__, line, device[*count], *count);
#endif
					(*count)++;
	
					break;
				}
			}

			if (line != NULL)
			{
				free(line);
			}
			fclose(fp);

			if(*count > MODEM_USBSERIAL_MAX_INDEX)
			{
				break;
			}
		}
		if(++i > MODEM_USBSERIAL_MAX_VALID_INDEX)
		{
			break;
		}
	}while(1);

	return ret;
}

int check_is_dm_dongle_by_modem_info(struct modem_info* info)
{
	//FIXME maybe will changed in future
	return info->pid_vid_now ==	0x19d2fff1 && info->at_port == 3;
}

int check_is_dm_dongle_by_path(int pid_vid, const char* at_port)
{
	//FIXME maybe will changed in future
	return pid_vid == 0x19d2fff1 && !strcmp(at_port, "/dev/ttyUSB3");
}

/*************************************************************/
// zte dm cmd set, it has independent with .
int dm_writeline_len(int fd, const char *s, unsigned int length)
{
	unsigned int i = 0;
    unsigned int cur = 0;
    unsigned int len = strlen(s);
    ssize_t written;

    if (fd < 0) {
        return AT_ERROR_CHANNEL_CLOSED;
    }
    
    if( length > 0 ) {
    	len = length;
    }
	
	for(i = 0; i < len; i++)
#ifdef ENABLE_LOG
    	ALOGD("AT>--%02x--", *(s+i));
#endif

    /* the main string */
    while (cur < len) {
        do {
            written = write (fd, s + cur, len - cur);
        } while (written < 0 && errno == EINTR);

        if (written < 0) {
            return AT_ERROR_GENERIC;
        }

        cur += written;
    }

    return 0;
}

int dm_read_timeout(int fd, void *buf, unsigned int nbytes, unsigned int timeout)
{
	int n = 0;
	int len = 0;
	fd_set readfds;
	struct timeval tv;

    if (fd < 0) {
        return AT_ERROR_CHANNEL_CLOSED;
    }
    	
	tv.tv_sec = 0;        // seconds
	tv.tv_usec = timeout; // microseconds
	
	FD_ZERO(&readfds);      // initial
	FD_SET(fd, &readfds); // add fd to readfds for monitoring fd
	
	n = select(fd+1, &readfds, NULL, NULL, &tv); // monitor fd status,the first para is fd+1 !
	if(n <= 0) {
		if(n == 0) {	
			errno = ETIME;
#ifdef ENABLE_LOG
			ALOGD(">>%s:select timeout...", __FUNCTION__);
#endif
		}
#ifdef ENABLE_LOG
		ALOGD(">>%s:select error...", __FUNCTION__);	
#endif
		return -1;
	}
#ifdef ENABLE_LOG
	ALOGD(">>%s:read...", __FUNCTION__);
#endif
	len = read(fd, buf, nbytes);
		
	return len;
}

void dm_dongle_initial(const char* path)
{
    int err;
	char *line = NULL;
	char *response = NULL;
	int len = 0;
	int ret = -1;
	
	int fd = open (path, O_RDWR);
	if (fd < 0) 
	{
#ifdef ENABLE_LOG
		ALOGD("Open %s failed, try others...", path);
#endif
	}
	/* disable echo on serial ports */
	struct termios  ios;
	tcgetattr( fd, &ios );
	ios.c_lflag = 0;  /* disable ECHO, ICANON, etc... */
	tcsetattr( fd, TCSANOW, &ios );

	// get esn	
	dm_writeline_len(fd, dm_esn_cmd, 4);
	len = dm_read_timeout(fd, zte_dm_buffer, 1024, 500000);// 500ms timeout
#ifdef ENABLE_LOG
	ALOGD("read len = %d; dm strlen = %d", len, zte_dm_strlen(zte_dm_buffer) );
#endif
	if( len >=8 ) {
#ifdef ENABLE_LOG
		ALOGD("ESN RSP:%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x.",
				zte_dm_buffer[0],zte_dm_buffer[1],zte_dm_buffer[2],zte_dm_buffer[3],
				zte_dm_buffer[4],zte_dm_buffer[5],zte_dm_buffer[6],zte_dm_buffer[7]);	
#endif
	}

	// get status
	dm_writeline_len(fd, dm_status_cmd, 7);
	len = dm_read_timeout(fd, zte_dm_buffer, 1024, 500000);// 500ms timeout
#ifdef ENABLE_LOG
	ALOGD("read len = %d; dm strlen = %d", len, zte_dm_strlen(zte_dm_buffer) );
#endif
	if( len >=17 ) {
#ifdef ENABLE_LOG
		ALOGD("ESN RSP:%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x.",
				zte_dm_buffer[0],zte_dm_buffer[1],zte_dm_buffer[2],zte_dm_buffer[3],
				zte_dm_buffer[4],zte_dm_buffer[5],zte_dm_buffer[6],zte_dm_buffer[7], 	
				zte_dm_buffer[8],zte_dm_buffer[9],zte_dm_buffer[10],zte_dm_buffer[11], 
				zte_dm_buffer[12],zte_dm_buffer[13],zte_dm_buffer[14],zte_dm_buffer[15],zte_dm_buffer[16]);
#endif
		// get roaming status  zte_dm_buffer[11] roaming indicator
		// get network type	   zte_dm_buffer[5]  cdma/hdr/amps
		// get service status  zte_dm_buffer[4]  online/offline/ftm mode
		// get rssi            zte_dm_buffer[12] CSQ rssi indicator    			
	}
	//zte ac2737 use ttyUSB0 as modem port	
	// echo ATE0 to disable echo function.
	{
		int times =0;
		char buf[10] ={0};
		int fd = open ("/dev/ttyUSB0", O_RDWR);	
		if ( fd >= 0 ) {
			/* disable echo on serial ports */
			struct termios  ios;
			tcgetattr( fd, &ios );
			ios.c_lflag = 0;  /* disable ECHO, ICANON, etc... */
			tcsetattr( fd, TCSANOW, &ios );
		} else {
#ifdef ENABLE_LOG
			ALOGD("Open %s failed, ...", path);
#endif
		}
		// times * 30 ms 
		times = 2;
		do{
			ret = write_line(fd, "ATE0");
#ifdef ENABLE_LOG
			ALOGD("write_line ret = %d", ret);
#endif
			//receive	response
			ret = read_timeout(fd, buf, 12, 30000);//30ms, what about the time?
#ifdef ENABLE_LOG
			ALOGD("read_timeout ret = %d", ret);	
#endif
			if((ret == -1) || (ret == ETIME))
				continue; // with no response
			else break;	  // with response, got it!        	
		}while( --times > 0 );

		close(fd);	// close 
	}
}
//---------
